#include "stdafx.h"
#include <functional>
#include "memrec.h"
#include "SegmentCache.h"

#define new DBG_NEW



tUnAlignedBufferChain::~tUnAlignedBufferChain()
{
	tUnAlignedBufferNode * pNode = m_pHead;
	while (pNode != NULL)
	{
		tUnAlignedBufferNode * pDel = pNode;
		pNode = pNode->m_pNext;
		pDel->DeAlloc();
		delete pDel;
	}
}

void tUnAlignedBufferChain::SetSegmentUnitSize(unsigned int Size)
{
	m_SegmentUnitSize = Size;
}

void tUnAlignedBufferChain::SetLastSegment(unsigned int Index, unsigned int Size)
{
	m_LastSegmentIndex = Index;
	m_LastSegmentSize = Size;
	m_DetectedLast = true;
}

eResult tUnAlignedBufferChain::InsertBuffer(tUnAlignedBuffer * pBuffer)
{
	UINT64 Index = pBuffer->BaseOffset / m_SegmentUnitSize;
	UINT64 AlignedOffset = (Index+1) * m_SegmentUnitSize;
	while (pBuffer->Left() != 0)
	{
		// Determine the length of small fragment.
		unsigned int CopySize = (unsigned int)(AlignedOffset - pBuffer->OffsetNow);
		if (CopySize > (unsigned int)pBuffer->Left())
			CopySize = (unsigned int)pBuffer->Left();
		assert(CopySize != 0);

		// Create a temp fragment and insert it to chain.
		tUnAlignedBuffer SmallFragment;
		ALLOC(&SmallFragment, CopySize);
		SmallFragment.Copy(0, pBuffer->CurrentPointer(), CopySize);
		SmallFragment.BaseOffset = SmallFragment.OffsetNow = pBuffer->OffsetNow;
		InsertBuffer2(&SmallFragment);
		SmallFragment.DeAlloc();

		// Update the main buffer.
		pBuffer->MovePointer(CopySize);
		pBuffer->OffsetNow += CopySize;
		AlignedOffset += m_SegmentUnitSize;
	}
	return RET_OK;
}

eResult tUnAlignedBufferChain::InsertBuffer2(tUnAlignedBuffer * pBuffer)
{
	pBuffer->OffsetNow = pBuffer->BaseOffset;
	tUnAlignedBufferNode * pNode = NULL;
	// search the OffsetNow position on the chain.
	while (pBuffer->OffsetNow != pBuffer->BaseOffset + pBuffer->Size())
	{
		eResult SearchResult = SearchOffsetPosition(pBuffer->OffsetNow, &pNode);
		if (SearchResult == RET_IN_CHAIN_NODE)
		{
			// Skip the data which is already filled in other buffer.
			assert(pNode != NULL);
			unsigned int SkipLength = pNode->BaseOffset + pNode->Size() - pBuffer->OffsetNow;
			if (SkipLength > (unsigned int)pBuffer->Left())
				SkipLength = (unsigned int)pBuffer->Left();
			pBuffer->OffsetNow += SkipLength;
			pBuffer->MovePointer(SkipLength);
			if (pBuffer->OffsetNow > pBuffer->BaseOffset + pBuffer->Size())
				pBuffer->OffsetNow = pBuffer->BaseOffset + pBuffer->Size();
		}
		else
		{
			if (pNode == NULL)
			{
				// Determine how long we can add to chain.
				unsigned int AddLen = (unsigned int)pBuffer->Left();
				if (m_pHead != NULL)
				{
					if (pBuffer->OffsetNow + AddLen > m_pHead->BaseOffset)
					{
						// Reduce the size.
						AddLen = m_pHead->BaseOffset - pBuffer->OffsetNow;
					}
				}
				
				// Add the new fragment at the head of chain.
				tUnAlignedBufferNode * pNewNode = new tUnAlignedBufferNode();
				ALLOC(pNewNode, AddLen);
				pNewNode->Copy(0, pBuffer->CurrentPointer(), AddLen);
				pBuffer->MovePointer(AddLen);
				pNewNode->BaseOffset = pBuffer->OffsetNow;
				pNewNode->OffsetNow = pBuffer->OffsetNow;
				// Connect the chain.
				pNewNode->m_pNext = m_pHead;
				m_pHead = pNewNode;
				// Move the next offsetNow.
				pBuffer->OffsetNow += AddLen;
			}
			else
			{
				// Determine how long we can add.
				unsigned int AddLen = (unsigned int)pBuffer->Left();
				if (pNode->m_pNext != NULL)
				{
					if (pBuffer->OffsetNow + AddLen > pNode->m_pNext->BaseOffset)
					{
						// Reduce the size.
						AddLen = pNode->m_pNext->BaseOffset - pBuffer->OffsetNow;
					}
				}

				// Add the fragment to middle or tail of the chain.
				tUnAlignedBufferNode * pNewNode = new tUnAlignedBufferNode();
				ALLOC(pNewNode, AddLen);
				pNewNode->Copy(0, pBuffer->CurrentPointer(), AddLen);
				pBuffer->MovePointer(AddLen);
				pNewNode->BaseOffset = pBuffer->OffsetNow;
				pNewNode->OffsetNow = pBuffer->OffsetNow;
				// Connect the chain.
				pNewNode->m_pNext = pNode->m_pNext;
				pNode->m_pNext = pNewNode;
				// Move the next offset.
				pBuffer->OffsetNow += AddLen;
			}
		}
	}
	return RET_OK;
}

eResult tUnAlignedBufferChain::SearchOffsetPosition(unsigned int OffsetNow, tUnAlignedBufferNode ** ppNode)
{
	tUnAlignedBufferNode * pNode = m_pHead;
	tUnAlignedBufferNode * pPrev = NULL;
	while (pNode != 0)
	{
		if (OffsetNow < pNode->BaseOffset)
		{
			// The fragment is before this node.
			*ppNode = pPrev;
			return RET_OUT_CHAIN_NODE;
		}
		else if (OffsetNow >= pNode->BaseOffset && OffsetNow < pNode->BaseOffset + pNode->Size())
		{
			// In the middle of the node.
			*ppNode = pNode;
			return RET_IN_CHAIN_NODE;
		}
		else
		{
			// Go to next node.
			pPrev = pNode;
			pNode = pNode->m_pNext;
		}
	}
	// Meet the tail of the chain, prev(tail) is the right node.
	*ppNode = pPrev;
	return RET_OUT_CHAIN_NODE;
}

eResult tUnAlignedBufferChain::FindCompletedSegment(std::vector<tSegment*> &refSegmentList)
{
	tUnAlignedBufferNode* pStart, *pNode, *pNext, *pPrevStart = NULL;
	unsigned int ContinousLength = 0;
	pStart = pNode = m_pHead;
	while (pNode != NULL)
	{
		// Find the fragment which is aligned with segment.
		if (pNode->BaseOffset % m_SegmentUnitSize != 0)
		{
			pPrevStart = pNode;
			pStart = pNode = pNode->m_pNext;
			continue;
		}

		// Calcuate how long the fragments can be combined.
		while (pNode != NULL)
		{
			pNext = pNode->m_pNext;
			ContinousLength += (unsigned int)pNode->Size();
			bool IsLastSeg = false;
			if (m_DetectedLast &&
				pStart->BaseOffset == m_SegmentUnitSize * m_LastSegmentIndex)
			{
				IsLastSeg = true;
			}
			if ((!IsLastSeg && ContinousLength >= m_SegmentUnitSize) ||
				(IsLastSeg && ContinousLength >= m_LastSegmentSize))
			{
				tSegment * pNewSegment = new tSegment();
				pStart = pNode = MergeToSegment(pStart, pPrevStart, pNewSegment);
				refSegmentList.push_back(pNewSegment);
				ContinousLength = 0;
				// jump out this loop for next aligned fragment from returned pNode.
				break;
			}
			else
			{
				if (pNext == NULL || pNode->BaseOffset + pNode->Size() == pNext->BaseOffset)
				{
					// Accumulate the next framgment.
					pNode = pNode->m_pNext;
				}
				else
				{
					// discontinous. reset the start node and continous length.
					pPrevStart = pNode;
					pStart = pNode = pNode->m_pNext;
					ContinousLength = 0;
					break;
				}
			}			
		}
	}
	return RET_OK;
}

tUnAlignedBufferNode * tUnAlignedBufferChain::MergeToSegment(tUnAlignedBufferNode * pNode, tUnAlignedBufferNode * pPrev, tSegment * pSegment)
{
	tUnAlignedBufferNode * pStart = pNode;
	tUnAlignedBufferNode * pEnd = NULL;
	assert(pNode->BaseOffset % m_SegmentUnitSize == 0);
	
	pSegment->Index = pNode->BaseOffset / m_SegmentUnitSize;
	if (pSegment->Index == m_LastSegmentIndex && m_DetectedLast)
	{
		ALLOC(pSegment, m_LastSegmentSize);
	}
	else
	{
		ALLOC(pSegment, m_SegmentUnitSize);
	}
	while (pSegment->Left() != 0)
	{
		unsigned int CopySize = (unsigned int)pSegment->Left();
		if (CopySize > (unsigned int)pNode->Left())
			CopySize = (unsigned int)pNode->Left();
		pSegment->Append(pNode->CurrentPointer(), CopySize);
		pNode->MovePointer(CopySize);
		if (pNode->Left() == 0)
			pNode = pNode->m_pNext;
	}
	pEnd = pNode;
	
	// Maintain the chain.
	if (pPrev == NULL)
		m_pHead = pEnd;
	else
		pPrev->m_pNext = pEnd;	
	
	pNode = pStart;
	while (pNode != pEnd)
	{
		tUnAlignedBufferNode * pDel = pNode;
		pNode = pNode->m_pNext;
		pDel->DeAlloc();
		delete pDel;
	}
	return pEnd;
}

tUnAlignedBufferNode * tUnAlignedBufferChain::GetHead()
{
	return m_pHead;
}

void tUnAlignedBufferChain::PopHead()
{
	tUnAlignedBufferNode * pNode = m_pHead;
	if (pNode)
	{
		pNode->DeAlloc();
		m_pHead = pNode->m_pNext;
		delete pNode;
	}
}

 
tCachedSegment::tCachedSegment(const tCachedSegment& seg)
{
	HitCount = seg.HitCount;
	SegmentCopy(seg);
}

tCachedSegment::tCachedSegment(const tSegment& seg):
HitCount(0)
{
	SegmentCopy(seg);
}

tCachedSegment& tCachedSegment::operator=(const tCachedSegment& seg)
{
	HitCount = seg.HitCount;
	SegmentCopy(seg);
	return *this;
}

 
tCachedSegment& tCachedSegment::operator=(const tSegment& seg)
{
	HitCount = 0;
	SegmentCopy(seg);
	return *this;
}

tMemSegmentCache::tMemSegmentCache():
MaxEntry(DEFAULT_MAX_SEGMENT_ENTRY)
{

}

 
tMemSegmentCache::~tMemSegmentCache()
{
	std::map<UINT64, tCachedSegment>::iterator it;
	for (it = SegmentMap.begin(); it != SegmentMap.end(); it++)
	{
		tCachedSegment& CachedSegment = it->second;
		CachedSegment.DeAlloc();
	}
	SegmentMap.clear();
}


eResult tMemSegmentCache::ReadSegment(UINT64 Index, tSegment& Segment)
{
	
	std::map<UINT64, tCachedSegment>::iterator it;
	it = SegmentMap.find(Index);
	if (it == SegmentMap.end())
	{
#ifndef _linux_
//		Sleep(1);
#endif
		return RET_ERROR;
	}
	if (Segment.Size() == 0)
		Segment = it->second;
	else
	{
		// Only copy.
		Segment.Index = it->second.Index;
		if (Segment.Size() != it->second.Size())
		{
			Segment.size = it->second.Size();
			return RET_SIZE_ERROR;
		}
	}
	// Put this Segment to cache.
	it->second.AddHitCount();
	return RET_OK;
}

 
eResult tMemSegmentCache::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	if (SegmentMap.size() >= MaxEntry)
		return RET_ERROR;
	SegmentMap[Index] = Segment;
	return RET_OK;
}

eResult tMemSegmentCache::IsSegmentExist(UINT64 Index)
{
	std::map<UINT64, tCachedSegment>::iterator it;
	it = SegmentMap.find(Index);
	if (it != SegmentMap.end())
		return RET_OK;
	else
		return RET_ERROR;
}

unsigned int tMemSegmentCache::GetCachedCount()
{
	return (unsigned int)SegmentMap.size();
}

unsigned int tMemSegmentCache::GetCachedSize()
{
	sum_memcache sum = std::for_each(SegmentMap.begin(), SegmentMap.end(), sum_memcache());
	return sum.m_CachedSize;
}

unsigned int tMemSegmentCache::FlushToDiskCache(tSegmentCache * pDiskCache) 
{
	unsigned int WriteSegNum = 0;
	std::map<UINT64, tCachedSegment>::iterator it;
	for (it = SegmentMap.begin(); it != SegmentMap.end(); it++)
	{
		tCachedSegment& CachedSegment = it->second;
		if (pDiskCache->WriteSegment(CachedSegment.Index, CachedSegment) == RET_OK)
		{
			WriteSegNum++;
		}
		CachedSegment.DeAlloc();
	}
	SegmentMap.clear();
	return WriteSegNum;
}

 
eResult tReadMemSegmentCache::ReadSegment(UINT64 Index, tSegment& Segment)
{
	return tMemSegmentCache::ReadSegment(Index, Segment);
}


eResult tReadMemSegmentCache::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	if (SegmentMap.size() == MaxEntry)
	{
		//Release one segment.
		int HitCountMin = 1000000;
		std::map<UINT64, tCachedSegment>::iterator it_delete = SegmentMap.begin();
		std::map<UINT64, tCachedSegment>::iterator it;
		for (it = SegmentMap.begin(); it != SegmentMap.end(); it++)
		{
			if (it->second.HitCount < HitCountMin)
			{
				it_delete = it;
				HitCountMin = it->second.HitCount;
			}
		}
		it_delete->second.DeAlloc();
		SegmentMap.erase(it_delete);
	}
	 
	return tMemSegmentCache::WriteSegment(Index, Segment);

}

 
eResult tWriteMemSegmentCache::ReadSegment(UINT64 Index, tSegment& Segment)
{
	return tMemSegmentCache::ReadSegment(Index, Segment);
}

 
eResult tWriteMemSegmentCache::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	if (tMemSegmentCache::WriteSegment(Index, Segment) != RET_ERROR)
	{
		if (SegmentMap.size() == MaxEntry)
			return RET_FULL;
	}
	else
		return RET_ERROR;
	return RET_OK;
}

 
tSegmentIndexTable::tSegmentIndexTable() 
{
	IndexTable = new unsigned int[DEFAULT_INDEX_TABLE_SIZE];
	IndexRange = DEFAULT_INDEX_TABLE_SIZE;
	memset(IndexTable, 0xff, DEFAULT_INDEX_TABLE_SIZE * sizeof(unsigned int));
}

 
tSegmentIndexTable::~tSegmentIndexTable()
{
	if (IndexTable)
		delete[] IndexTable;
}

eResult tSegmentIndexTable::SetIndex(UINT64 Index, unsigned int Offset)
{
	if (Index >= IndexRange)
	{
		//Reallocate the memory.
		unsigned int * NewTable = new unsigned int[(unsigned int)Index + 100];
		if (NewTable == NULL)
		{
			assert(NewTable != NULL);
			return RET_ERROR;
		}

		memcpy(NewTable, IndexTable, sizeof(unsigned int) * IndexRange);
		memset(NewTable+IndexRange, 0xff, ((unsigned int)Index+100-IndexRange)*sizeof(unsigned int));
		IndexRange = (unsigned int)Index + 100;
		delete[] IndexTable;
		IndexTable = NewTable;
	}
	IndexTable[Index] = Offset;
	return RET_OK;
}

 
eResult tSegmentIndexTable::GetIndexValue(UINT64 Index, unsigned int & Offset)
{
	if (Index >= (UINT64)IndexRange)
		return RET_ERROR;
	Offset = IndexTable[Index];
	if (Offset == INVALID_OFFSET)
		return RET_ERROR;
	else
		return RET_OK;
}


eResult tSegmentIndexTable::AssignIndexRange(unsigned int * pIndexTable, unsigned int aIndexRange)
{
	 
	if (IndexRange <= aIndexRange)
	{
		if (IndexTable)
			delete[] IndexTable;
		IndexTable = new unsigned int[aIndexRange];
		if (IndexTable == NULL)
			return RET_ERROR;
		IndexRange = aIndexRange;
		memset(IndexTable, 0xff, IndexRange*sizeof(unsigned int));
	}
	else
	{
		memset(IndexTable, 0xff, IndexRange*sizeof(unsigned int));
		IndexRange = aIndexRange;
	}
	memcpy(IndexTable, pIndexTable, aIndexRange*sizeof(unsigned int));
	return RET_OK;
}

unsigned int * tSegmentIndexTable::GetIndexTable(unsigned int & outIndexRange)
{
	outIndexRange = IndexRange;
	return IndexTable;
}

void tSegmentIndexTable::TruncateIndexTable(unsigned int uMaxOffset)
{
	for (unsigned int i = 0; i < IndexRange; i++)
	{
		if (IndexTable[i] != INVALID_OFFSET && IndexTable[i] > uMaxOffset)
		{
			IndexTable[i] = INVALID_OFFSET;
		}
	}
}

 
tDiskSegmentCache::tDiskSegmentCache():
SegmentUnitSize(0),
FinalIndex(INVALID_INDEX),
FinalSegmentSize(0),
hCacheFile(0),
AppendPos(0),
m_CachedDataSize(0),
m_CachedSegmentCount(0)
{

}

 
tDiskSegmentCache::~tDiskSegmentCache()
{
	CloseCacheFile();
}

 
eResult tDiskSegmentCache::ReadSegment(UINT64 Index, tSegment& Segment)
{
	if (hCacheFile == 0)
		return RET_ERROR;
	unsigned int Offset = 0;
	eResult Res = pIndexTable->GetIndexValue(Index, Offset);
	if (Res != RET_OK)
		return Res;
	int opres = fseek(hCacheFile, Offset, SEEK_SET);
	if (opres != 0)
	{
		// Error on SegmentIndex table, correct it.
		pIndexTable->SetIndex(Index, INVALID_OFFSET);
		return RET_ERROR;
	}
	if (Index == INVALID_INDEX)
		return RET_ERROR;
	int SegSize = (Index == FinalIndex)?FinalSegmentSize:SegmentUnitSize;
	if (Segment.Size() == 0)
	{
		ALLOC(&Segment, SegSize);
	}
	else
	{
		if (Segment.Size() != SegSize)
		{
			Segment.size = SegSize;
			return RET_SIZE_ERROR;
		}
	}

	opres = (int)fread(Segment.GetBase(), 1, SegSize, hCacheFile);
	if (opres == SegSize)
	{
		Segment.Index = Index;
	}
	else
	{
		Segment.DeAlloc();
		return RET_ERROR;
	}
	return RET_OK;
}

 
eResult tDiskSegmentCache::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	if (hCacheFile == 0)
		return RET_ERROR;
	unsigned int Offset;
	eResult Res = pIndexTable->GetIndexValue(Index, Offset);
	if (Res == RET_OK)
	{
		// Over write the previous.
	}
	else
	{
		// Append to tail of the file.
		Offset = AppendPos;
	}
	fseek(hCacheFile, Offset, SEEK_SET);
	printf("Offset = %u\n", Offset);
	int opres = 0;
	if ((unsigned int)Segment.Size() < SegmentUnitSize)
	{
		tSegment RegSegment;
		ALLOC(&RegSegment, SegmentUnitSize);
		RegSegment.Copy(0, Segment.GetBase(), Segment.Size());
		opres = (int)fwrite(RegSegment.GetBase(), 1, RegSegment.Size(), hCacheFile);
	}
	else if (Segment.Size() == SegmentUnitSize)
		opres = (int)fwrite(Segment.GetBase(), 1, Segment.Size(), hCacheFile);
	else
	{
		MessageBoxA(NULL, "Internal Error: Cannot write data to cache file.\n", "Warning", MB_OK);
	}
	if (opres == SegmentUnitSize)
	{
		pIndexTable->SetIndex(Index, Offset);
		if (Offset == AppendPos)
		{
			AppendPos += SegmentUnitSize;
			m_CachedDataSize += (unsigned int)Segment.Size();
			m_CachedSegmentCount++;
		}
		return RET_OK;
	}
	else
	{
		pIndexTable->SetIndex(Index, INVALID_OFFSET);
		return RET_ERROR;
	}
}

eResult tDiskSegmentCache::IsSegmentExist(UINT64 Index)
{
	unsigned int Offset = 0;
	eResult Res = pIndexTable->GetIndexValue(Index, Offset); 
	return Res;
}

unsigned int tDiskSegmentCache::GetCachedCount()
{
	return 0;
}

 
eResult tDiskSegmentCache::OpenCacheFile(const char * filename)
{
	fopen_s(&hCacheFile, filename, "rb+");
	if (hCacheFile == NULL)
		return RET_ERROR;
	return RET_OK;
}

 
eResult tDiskSegmentCache::CreateCacheFile(const char * filename)
{
	fopen_s(&hCacheFile, filename, "wb+");
	if (hCacheFile == NULL)
		return RET_ERROR;
	return RET_OK;
}

 
eResult tDiskSegmentCache::CloseCacheFile() 
{
	if (hCacheFile)
	{
		fclose(hCacheFile);
	}
	hCacheFile = 0;
	return RET_OK;
}

unsigned int tDiskSegmentCache::GetDataSize()
{
	if (hCacheFile)
	{	
		fseek(hCacheFile, 0, SEEK_END);
		unsigned int data_size = ftell(hCacheFile);
		return data_size;
	}
	else
		return 0;
}

void tDiskSegmentCache::FlushFile()
{
	if (hCacheFile)
		fflush(hCacheFile);
}

 
void tDiskSegmentCache::InitCacheInfo(int aUnitSize, tSegmentIndexTable * pSIT, unsigned int SegCount, unsigned int CachedSize)
{
	SegmentUnitSize = aUnitSize;
	pIndexTable = pSIT;
	AppendPos = SegCount * SegmentUnitSize;
	m_CachedDataSize = CachedSize;
	m_CachedSegmentCount = SegCount;
}

void tDiskSegmentCache::SetFinalSegment(UINT64 aFinalIndex, int aFinalSegSize)
{
	FinalIndex = aFinalIndex;
	FinalSegmentSize = aFinalSegSize;
}

 
tContentCacheInfo::tContentCacheInfo(): 
ContentID(0),
TotalSize(0),
SegmentUnitSize(0),
FinalIndex(INVALID_INDEX),
FinalSegmentSize(0),
Usage(0),
LatestIndex(0),
SegCountOnDisk(0),
CacheDataSize(0)
{

}

 
eResult tContentCacheInfo::LoadFromFile(const char* filename, bool bOnlyHeader) 
{
	FILE * hFile = NULL;
	fopen_s(&hFile, filename, "rb");
	if (hFile == NULL)
		return RET_ERROR;
	fseek(hFile, 0, SEEK_END);
	long length = ftell(hFile);
	fseek(hFile, 0, SEEK_SET);
	long headerlength = GetHeaderLength();
	tStreamBuffer Header;
	ALLOC(&Header, headerlength);
	int readc = (int)fread(Header.GetBase(), 1, headerlength, hFile);
	if (readc != headerlength)
	{
		fclose(hFile);
		return RET_ERROR;
	}

	char tag[8];
	Header.GetData((PBYTE)tag, 8);
	if (memcmp(tag, CACHE_TAG,8) != 0)
	{
		fclose(hFile);
		return RET_ERROR;
	}
	ContentID.m_High64 = Header.GetInt8();
	ContentID.m_Low64 = Header.GetInt8();
	TotalSize = Header.GetInt4();
	Usage = Header.GetInt4();
	FinalIndex = Header.GetInt8();
	FinalSegmentSize = Header.GetInt4();
	SegmentUnitSize = Header.GetInt4();
	LatestIndex = Header.GetInt8();
	SegCountOnDisk = Header.GetInt4();
	CacheDataSize = Header.GetInt4();

	if (TotalSize != 0)
	{
		FinalSegmentSize = TotalSize % SegmentUnitSize;
		FinalIndex = (TotalSize - FinalSegmentSize) / SegmentUnitSize;
	}
	
	m_strCacheInfoFile = filename;
	
	long index_table_size = length - headerlength;
	if ((index_table_size & 0x3) != 0)
	{
		//bad file structure, turncate the size.
		index_table_size &= 0xfffffffc;
	}
	unsigned int * Buf = new unsigned int[index_table_size/4];
	if (Buf == NULL)
	{
		fclose(hFile);
		return RET_ERROR;
	}
	memset(Buf, 0, index_table_size);
	fread(Buf, 1, index_table_size, hFile);

	// Check the Saved Segment Count in Disk and Save Data Size.
	SegCountOnDisk = 0;
	CacheDataSize = 0;
	for (unsigned int index = 0; index < index_table_size/4; index++)
	{
		if (Buf[index] != INVALID_INDEX)
		{
			SegCountOnDisk++;
			if (index == FinalIndex && FinalIndex != INVALID_INDEX)
			{
				CacheDataSize += FinalSegmentSize;
			}
			else
			{
				CacheDataSize += SegmentUnitSize;
			}
		}
	}

	if (bOnlyHeader)
	{
		delete[] Buf;
		fclose(hFile);
		return RET_OK;
	}	
	eResult res = IndexTable.AssignIndexRange(Buf, index_table_size/4);
	delete[] Buf;
	fclose(hFile);
	return res;
}

 
eResult tContentCacheInfo::WriteToFile(const char* filename)
{
	int headerlength = GetHeaderLength();
	tStreamBuffer Header;
	ALLOC(&Header,headerlength);
	char tag[8];
	memcpy(tag, CACHE_TAG, 8);
	Header.Append((PBYTE)tag, 8);
	Header.WriteInt8(ContentID.m_High64);
	Header.WriteInt8(ContentID.m_Low64);
	Header.WriteInt4(TotalSize);
	Header.WriteInt4(Usage);
	Header.WriteInt8(FinalIndex);
	Header.WriteInt4(FinalSegmentSize);
	Header.WriteInt4(SegmentUnitSize);
	Header.WriteInt8(LatestIndex);
	Header.WriteInt4(SegCountOnDisk);
	Header.WriteInt4(CacheDataSize);

	unsigned int IndexTableSize;
	unsigned int * pIndexTable = IndexTable.GetIndexTable(IndexTableSize);
	IndexTableSize *=sizeof(unsigned int);
	HANDLE hFile = CreateFileA(filename, 
								FILE_ALL_ACCESS,
								FILE_SHARE_READ,
								NULL,
								OPEN_ALWAYS,
								0,
								NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
		DWORD Written = 0;
		WriteFile(hFile, Header.GetBase(), Header.Size(), &Written, NULL);
		WriteFile(hFile, pIndexTable, IndexTableSize, &Written, NULL);
		CloseHandle(hFile);
	}
	/*
	FILE * hFile = NULL;
	fopen_s(&hFile, filename, "wb");
	if (hFile == NULL)
	{
		return RET_ERROR;
	}
	fwrite(Header.GetBase(), 1, Header.Size(), hFile);
	fwrite(pIndexTable, 1, IndexTableSize, hFile);
	fclose(hFile);
	*/
	return RET_OK;
}

 
int tContentCacheInfo::GetHeaderLength() 
{
	return (ContentID.size() + \
			sizeof(TotalSize) + \
			sizeof(Usage) + \
			sizeof(FinalIndex) + \
			sizeof(FinalSegmentSize) + \
			sizeof(SegmentUnitSize) + \
			sizeof(LatestIndex) + \
			sizeof(SegCountOnDisk) + \
			sizeof(CacheDataSize) + \
			8)/*tag*/;
}

//====================================================================
// The tContentCacheItem is not used in Linux version currently. So we
// remark all the source codes when _linux_ is defined, Until the time
// when we need this class under linux.
//====================================================================
#ifndef _linux_

 
tContentCacheItem::tContentCacheItem() :
tBaseInterface("tContentCacheItem"),
m_uTruncateSegCount(0),
m_FreshDataSize(0),
m_FreshReceiveDataSize(0),
m_DeleteMark(false),
m_bModified(false)
{
	hMutex = CreateMutex(NULL, FALSE, NULL);
	m_ExpressLock = CreateMutex(NULL, FALSE, NULL);
	InfoFileName[0] = 0;
	DataFileName[0] = 0;
#ifdef _SERVER_TEST
	bNoDiskCache = true;
#else
	bNoDiskCache = false;
#endif
	SetInterface(eIID_IBaseInterface, (IDataCacheItem*)this);
	SetInterface(eIID_DataCacheItem, (IDataCacheItem*)this);
	SetInterface(eIID_CacheBufferLeak, (ICacheBufferLeak*)this);
}

 
tContentCacheItem::~tContentCacheItem()
{
	DiskCache.CloseCacheFile();
	if (CacheItemInfo.SegCountOnDisk == 0)
	{
		remove(DataFileName);
		remove(InfoFileName);
	}
	m_ExpressSegment.DeAlloc();
	CloseHandle(m_ExpressLock);
	CloseHandle(hMutex);
}

eResult tContentCacheItem::ReadData(char * pBuffer, unsigned int Offset, unsigned int BufferLength, 
				unsigned int * pReadCount, bool bWait, unsigned int Timeout, HANDLE hBreakEvent)
{
	UINT64 Index = Offset / CacheItemInfo.SegmentUnitSize;
	unsigned int OffsetFromSeg = Offset % CacheItemInfo.SegmentUnitSize;
	tStreamBuffer FillBuffer;
	FillBuffer.refto((PBYTE)pBuffer, BufferLength);
	//DBGSTR1("Read Data Index = %llu.\n", Index);
	// If the Index is zero, we first try the Express segment.
	if (Index == 0 && DetectSegment(0) == RET_ERROR)
	{
		unsigned int ReadOffset = Offset;
		unsigned int EndOffset = Offset + BufferLength;
		AUTO_MUTEX lock(m_ExpressLock);
		while (ReadOffset < (unsigned int)m_ExpressSegment.Size())
		{
			// Don't have enough data.
			if (ReadOffset >= (unsigned int)m_ExpressSegment.Offset())
			{
				lock.Release();
				if (bWait)
				{
					if (hBreakEvent)
					{
						DWORD res = WaitForSingleObject(hBreakEvent, 200);
						if (res == WAIT_OBJECT_0)
						{
							*pReadCount = (unsigned int)FillBuffer.Offset();
							return RET_BREAK;
						}
					}
					else
					{
						Sleep(200);
					}
				}
				else
				{
					*pReadCount = (unsigned int)FillBuffer.Offset();
					return RET_ERROR;
				}		
				// If the Segment have been written to normal space,
				// we goto use it.
				if (DetectSegment(0) == RET_OK)
					break;
				lock.Lock(INFINITE);
				continue;
			}
			else
			{			
				// Copy data for necessary.
				unsigned int cp_size = (unsigned int)m_ExpressSegment.Offset() - ReadOffset;
				if (cp_size > EndOffset - ReadOffset)
					cp_size = EndOffset - ReadOffset;
				FillBuffer.Append(m_ExpressSegment.GetBase() + ReadOffset, cp_size);
				if (FillBuffer.Left() == 0)
				{
					break;
				}
				ReadOffset += cp_size;
			}
		}
		if (ReadOffset >= (unsigned int)m_ExpressSegment.Size())
		{
			Index++;
			OffsetFromSeg = 0;
		}
	}

	tSegment Seg;
	while (FillBuffer.Left() != 0)
	{
		AUTO_MUTEX lock(hMutex);
		unsigned int nCpSize = 0;
		eResult Res = ReadSegment(Index, Seg);
		if (Res == RET_OK || Res == RET_LASTSEG)
		{
			nCpSize = (unsigned int)Seg.Size() - OffsetFromSeg;
			if (nCpSize > (unsigned int)FillBuffer.Left())
				nCpSize = (unsigned int)FillBuffer.Left();
			FillBuffer.Append(Seg.GetBase() + OffsetFromSeg, nCpSize);
			if (Res == RET_LASTSEG && nCpSize == Seg.Size() - OffsetFromSeg)
			{
				// Meet the end of the buffer, return directly, even if the 
				// data can not be fully read out.
				*pReadCount = (unsigned int)FillBuffer.Offset();
				Seg.DeAlloc();
				return RET_LASTSEG;
			}
			Seg.DeAlloc();
			OffsetFromSeg = 0;
			Index++;
		}
		else
		{
			if (bWait)
			{
				tReadOperation * pOperation = new tReadOperation();
				pOperation->m_BufferLength = BufferLength;
				pOperation->m_Offset = Offset;
				m_ReadQueue.push_back(pOperation);
				lock.Release();
				DWORD res = 0;
				if (hBreakEvent)
				{
					HANDLE waitlist[2];
					waitlist[0] = pOperation->m_hEvent;
					waitlist[1] = hBreakEvent;
					res = WaitForMultipleObjects(2, waitlist, FALSE, /*Timeout*/100000);
				}
				else
				{
					res = WaitForSingleObject(pOperation->m_hEvent, /*Timeout*/100000);
				}
				lock.Lock(INFINITE);
				
				// Remove the operation item in list.
				std::deque<tReadOperation*>::iterator it_Read = 
						std::find(m_ReadQueue.begin(), m_ReadQueue.end(), pOperation);
				if (it_Read != m_ReadQueue.end())
				{
					delete *it_Read;
					m_ReadQueue.erase(it_Read);
				}

				if (res == WAIT_OBJECT_0)
				{
					continue;
				}
				else if (res == WAIT_OBJECT_0 + 1)
				{
					*pReadCount = (unsigned int)FillBuffer.Offset();
					return RET_BREAK;
				}
				else if (res == WAIT_TIMEOUT)
				{
					if (Timeout == INFINITE)
						continue;
					else
					{
						*pReadCount = (unsigned int)FillBuffer.Offset();
						return RET_TIMEOUT;
					}
				}
				else
				{
					assert(false);
					*pReadCount = (unsigned int)FillBuffer.Offset();
					return RET_ERROR;
				}
			}
			else
			{
				break;
			}
		}
	}
	*pReadCount = (unsigned int)FillBuffer.Offset();
	return RET_OK;
}

eResult tContentCacheItem::WriteData(const char * pBuffer, unsigned int Offset, unsigned int BufferLength, bool bFinal)
{
	AUTO_MUTEX lock(hMutex);
	m_bModified = true;
	tUnAlignedBuffer InputBuffer;
	InputBuffer.refto((PBYTE)pBuffer, BufferLength);
	InputBuffer.BaseOffset = InputBuffer.OffsetNow = Offset;
	AddToFragmentChain(InputBuffer);
	ComposeSegmentFromFragments();

	CheckPendingOperation();


	return RET_ERROR;
}

eResult tContentCacheItem::WriteExpressData(const char * pBuffer, unsigned int Offset, unsigned int BufferLength)
{
	// If this data is in segment 0, we also put it
	// to the express segment buffer.
	AUTO_MUTEX Explock(m_ExpressLock);
	if (Offset < CacheItemInfo.SegmentUnitSize && 
		Offset == (unsigned int)m_ExpressSegment.Offset())
	{
		unsigned int size = BufferLength;
		if (size > (unsigned int)m_ExpressSegment.Left())
			size = (unsigned int)m_ExpressSegment.Left();
		m_ExpressSegment.Append((PBYTE)pBuffer, size);
	}
	return RET_OK;
}

eResult tContentCacheItem::AddToFragmentChain(tUnAlignedBuffer & InputBuffer)
{
	return m_FragmentChain.InsertBuffer(&InputBuffer);
}

eResult tContentCacheItem::ComposeSegmentFromFragments()
{
	std::vector<tSegment*> SegmentList;
	unsigned int Count = 0;
	m_FragmentChain.FindCompletedSegment(SegmentList);
	for (unsigned int i = 0; i < SegmentList.size(); i++)
	{
		WriteSegment(SegmentList[i]->Index, *SegmentList[i]);
		printf("Write segment: %d\n", SegmentList[i]->Index);
		SegmentList[i]->DeAlloc();
		delete SegmentList[i];
	}
	SegmentList.clear();
	return RET_OK;
}

void tContentCacheItem::CheckPendingOperation()
{
	tReadOperation * pOperation = NULL;
	std::deque<tReadOperation*>::iterator it_Operation;
	for (it_Operation = m_ReadQueue.begin(); it_Operation != m_ReadQueue.end(); it_Operation++)
	{
		pOperation = *it_Operation;
		UINT64 Index = pOperation->m_Offset / CacheItemInfo.SegmentUnitSize;
		bool bCompleted = false;
		if (DetectSegment(Index) == RET_OK)
		{
			SetEvent(pOperation->m_hEvent);
			break;
		}
	}
}

unsigned int tContentCacheItem::GetSegmentUnitSize()
{
	return CacheItemInfo.SegmentUnitSize;
}

eResult tContentCacheItem::GetSegment(UINT64 Index, tSegment& Seg)
{
	return ReadSegment(Index, Seg);
}

eResult tContentCacheItem::FreeSegment(tSegment & Seg)
{
	Seg.DeAlloc();
	return RET_OK;
}

eResult tContentCacheItem::SetSegment(UINT64 Index, const tSegment& Seg)
{
	return WriteSegment(Index, Seg);
}

eResult tContentCacheItem::DetectSegment(UINT64 Index)
{
	return IsSegmentExist(Index);
}

eResult tContentCacheItem::ReadSegment(UINT64 Index, tSegment& Segment)
{
	AUTO_MUTEX lock(hMutex);

	eResult ReturnValue;
	if (Index == CacheItemInfo.FinalIndex)
	{
		ReturnValue = RET_LASTSEG;
	}
	else
	{
		ReturnValue = RET_OK;
	}
	//1. Try MemoryReadCache
	eResult Res = MemoryReadCache.ReadSegment(Index, Segment);
	if (Res == RET_OK)
	{
		return ReturnValue;
	}
	//2. Try MemoryWriteCache
	Res = MemoryWriteCache.ReadSegment(Index, Segment);
	if (Res == RET_OK)
	{
		// Put this Segment to MemoryReadCache.
		MemoryReadCache.WriteSegment(Index, Segment);
		return ReturnValue;
	}

	if (bNoDiskCache)
		return RET_ERROR;

	//3. Try DiskCache
	Res = DiskCache.ReadSegment(Index, Segment);
	if (Res == RET_OK)
	{
		// Put this Segment to MemoryReadCache.
		MemoryReadCache.WriteSegment(Index, Segment);
		return ReturnValue;
	}
	// Fail to read segment.
	return RET_ERROR;
}

 
eResult tContentCacheItem::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	AUTO_MUTEX lock(hMutex);
	m_bModified = true;
	//DBGSTR1("Write Segment : Index = %llu.\n", Index);
	tSegment ModiSeg;
	eResult Res;
	if (Segment.Index == CacheItemInfo.FinalIndex &&
		Segment.Size() != CacheItemInfo.FinalSegmentSize)
	{
		ModiSeg.Index = Segment.Index;
		ALLOC(&ModiSeg, CacheItemInfo.FinalSegmentSize);
		unsigned int cpsize = __min((unsigned int)Segment.Size(), CacheItemInfo.FinalSegmentSize);
		ModiSeg.Copy(0, Segment.GetBase(), cpsize);
		// Write Segment to MemoryWriteCache.
		Res = MemoryWriteCache.WriteSegment(Index, ModiSeg);
	}
	else
	{
		// Write Segment to MemoryWriteCache.
		Res = MemoryWriteCache.WriteSegment(Index, Segment);
	}
	m_FreshDataSize += (unsigned int)Segment.Size();
	if (bNoDiskCache)
		return Res;

	if (Res == RET_FULL)
	{
		// Write All Segment in MemoryWriteCache to DiskCache and Clear the 
		// MemoryWriteCache.
		unsigned int n = MemoryWriteCache.FlushToDiskCache(&this->DiskCache);
		CacheItemInfo.SegCountOnDisk = DiskCache.m_CachedSegmentCount;
		DiskCache.FlushFile();
		DiskCache.CloseCacheFile();
		DiskCache.OpenCacheFile(DataFileName);
	}

	// update index range.
	IndexRange.InsertPair(Index, Index);
	CacheItemInfo.LatestIndex = Index;
	CacheItemInfo.CacheDataSize = DiskCache.m_CachedDataSize;

	// Auto save.
	m_FreshReceiveDataSize += (unsigned int)Segment.Size();
	if (m_FreshReceiveDataSize > AUTO_SAVE_DATASIZE)
	{
		SaveData();
		m_FreshReceiveDataSize = 0;
	}
	if (CacheItemInfo.CacheDataSize  + MemoryWriteCache.GetCachedSize() >= CacheItemInfo.TotalSize && 
				CacheItemInfo.TotalSize != 0)
	{
		SaveData();
		m_FreshReceiveDataSize = 0;
	}
	return RET_OK;
}


eResult tContentCacheItem::IsSegmentExist(UINT64 Index)
{
	AUTO_MUTEX lock(hMutex);
	//Find it in Read memory cache.
	eResult Res;
	Res = MemoryReadCache.IsSegmentExist(Index);
	if (Res != RET_OK)
	{
		Res = MemoryWriteCache.IsSegmentExist(Index);
		if (Res != RET_OK)
		{
			return DiskCache.IsSegmentExist(Index);
		}
	}
	return Res;
}

unsigned int tContentCacheItem::GetCachedCount()
{
	return 0;
}

unsigned int tContentCacheItem::GetDataSize()
{
	return CacheItemInfo.CacheDataSize;
}

eResult tContentCacheItem::InitCacheItem(tInt128 ContID, int SegUnitSize,
										 const char * infofile, const char * datafile)
{
	AUTO_MUTEX lock(hMutex);

	CacheItemInfo.ContentID = ContID;
	CacheItemInfo.SegmentUnitSize = SegUnitSize;
	m_FragmentChain.SetSegmentUnitSize(SegUnitSize);
	if (!bNoDiskCache)
	{
		lstrcpyA(InfoFileName, infofile);
		lstrcpyA(DataFileName, datafile);
		DiskCache.InitCacheInfo(SegUnitSize, &CacheItemInfo.IndexTable, 0, 0);
		DiskCache.CreateCacheFile(DataFileName);
		ALLOC(&m_ExpressSegment, SegUnitSize);
	}
	return RET_OK;
}

 
eResult tContentCacheItem::SetFinalSegment(UINT64 Index, unsigned int Size)
{
	AUTO_MUTEX lock(hMutex);

	CacheItemInfo.FinalIndex = Index;
	CacheItemInfo.FinalSegmentSize = Size;
	CacheItemInfo.TotalSize = CacheItemInfo.FinalIndex * CacheItemInfo.SegmentUnitSize + Size;
	m_FragmentChain.SetLastSegment((unsigned int)Index, Size);
	if (!bNoDiskCache)
		DiskCache.SetFinalSegment(Index, Size);
	return RET_OK;
}

UINT64 tContentCacheItem::GetFinalSegment(unsigned int * pFinalSegSize)
{
	if (pFinalSegSize)
		*pFinalSegSize = CacheItemInfo.FinalSegmentSize;
	return CacheItemInfo.FinalIndex;
}

void tContentCacheItem::GetIndexRange(tIndexRange * pIR)
{
	AUTO_MUTEX lock(hMutex);
	*pIR = IndexRange;
	//assert(IndexRange.IndexPairArray != 0);
}

UINT64 tContentCacheItem::GetLatestSegmentIndex()
{
	AUTO_MUTEX lock(hMutex);
	return CacheItemInfo.LatestIndex;
}

unsigned int tContentCacheItem::GetTotalSize()
{
	AUTO_MUTEX lock(hMutex);
	return CacheItemInfo.TotalSize;
}

void tContentCacheItem::SetTotalSize(unsigned int size)
{
	AUTO_MUTEX lock(hMutex);
	CacheItemInfo.TotalSize = size;
	if (size != 0)
	{
		CacheItemInfo.FinalSegmentSize = size % CacheItemInfo.SegmentUnitSize;
		CacheItemInfo.FinalIndex = 
			(size - CacheItemInfo.FinalSegmentSize) / CacheItemInfo.SegmentUnitSize;
		DiskCache.SetFinalSegment(
								CacheItemInfo.FinalIndex,
								CacheItemInfo.FinalSegmentSize
								);
		m_FragmentChain.SetLastSegment(
									(unsigned int)CacheItemInfo.FinalIndex, 
									CacheItemInfo.FinalSegmentSize
									);
	}
}

tInt128 tContentCacheItem::GetContentID()
{
	AUTO_MUTEX lock(hMutex);
	return CacheItemInfo.ContentID;
}
 
unsigned int tContentCacheItem::GetFreshDatasize()
{
	return m_FreshDataSize;
}

void tContentCacheItem::LeakFreshData(unsigned int size)
{
	AUTO_MUTEX lock(hMutex);
	if (m_FreshDataSize < size)
		m_FreshDataSize = 0;
	else
		m_FreshDataSize -= size;
}

void tContentCacheItem::FlushFreshData()
{
	AUTO_MUTEX lock(hMutex);
	m_FreshDataSize = 0;
}

eResult tContentCacheItem::LoadFromFile(const char * infofile, const char * datafile)
{
	AUTO_MUTEX lock(hMutex);
	if (bNoDiskCache)
		return RET_ERROR;
	eResult Res = CacheItemInfo.LoadFromFile(infofile);
	if (Res != RET_OK)
		return Res;
	DiskCache.InitCacheInfo(CacheItemInfo.SegmentUnitSize, 
							&CacheItemInfo.IndexTable,
							CacheItemInfo.SegCountOnDisk,
							CacheItemInfo.CacheDataSize);
	DiskCache.SetFinalSegment(CacheItemInfo.FinalIndex, CacheItemInfo.FinalSegmentSize);
	m_FragmentChain.SetSegmentUnitSize(CacheItemInfo.SegmentUnitSize);
	m_FragmentChain.SetLastSegment((unsigned int)CacheItemInfo.FinalIndex, CacheItemInfo.FinalSegmentSize);
	ALLOC(&m_ExpressSegment, CacheItemInfo.SegmentUnitSize);
	Res = DiskCache.OpenCacheFile(datafile); 
	if (Res != RET_OK)
		return Res;
	lstrcpyA(InfoFileName, infofile);
	lstrcpyA(DataFileName, datafile);
	return RET_OK;
}

unsigned int tContentCacheItem::TruncateCache(unsigned int uSize)
{
	unsigned int TotalSegCount = CacheItemInfo.SegCountOnDisk + MemoryWriteCache.GetCachedCount();
	unsigned int TruncateSegCount = (uSize + CacheItemInfo.SegmentUnitSize - 1) / CacheItemInfo.SegmentUnitSize;
	if (TotalSegCount >= TruncateSegCount)
	{
		// Have enough segment to free.
		m_uTruncateSegCount = TruncateSegCount;
		return m_uTruncateSegCount * CacheItemInfo.SegmentUnitSize;
	}
	else
	{
		// don't have so many segment to free.
		m_uTruncateSegCount = TotalSegCount;
		return TotalSegCount * CacheItemInfo.SegmentUnitSize;
	}
}

 
eResult tContentCacheItem::SaveToFile() 
{
	AUTO_MUTEX lock(hMutex);
	if (bNoDiskCache || !m_bModified)
		return RET_OK;

	// Must save the Segment Data in the MemoryWriteCache.
	unsigned int n = MemoryWriteCache.FlushToDiskCache(&DiskCache);
	DiskCache.FlushFile();
	DiskCache.CloseCacheFile();
	CacheItemInfo.SegCountOnDisk = DiskCache.m_CachedSegmentCount - m_uTruncateSegCount;
	CacheItemInfo.CacheDataSize = DiskCache.m_CachedDataSize;
	// Truncate the data file to SegCountOnDisk.
	HANDLE hFile = CreateFileA(DataFileName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ,
								NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
		LARGE_INTEGER iMove;
		iMove.QuadPart = CacheItemInfo.SegCountOnDisk * CacheItemInfo.SegmentUnitSize;
		if (SetFilePointerEx(hFile, iMove, NULL, FILE_BEGIN) && SetEndOfFile(hFile))
		{
			CloseHandle(hFile);
			// Modify the SegmentIndexTable.
			unsigned int uMaxOffset = (CacheItemInfo.SegCountOnDisk-1) * CacheItemInfo.SegmentUnitSize;
			CacheItemInfo.IndexTable.TruncateIndexTable(uMaxOffset);
			return CacheItemInfo.WriteToFile(InfoFileName);
		}
		CloseHandle(hFile);
	}
	//CacheItemInfo.SegCountOnDisk += m_uTruncateSegCount;
	return CacheItemInfo.WriteToFile(InfoFileName);
}

void tContentCacheItem::SaveData()
{
	AUTO_MUTEX lock(hMutex);
	if (bNoDiskCache || !m_bModified)
		return;
	// Must save the Segment Data in the MemoryWriteCache.
	unsigned int n = MemoryWriteCache.FlushToDiskCache(&DiskCache);
	CacheItemInfo.SegCountOnDisk = DiskCache.m_CachedSegmentCount;
	CacheItemInfo.CacheDataSize = DiskCache.m_CachedDataSize;
	DiskCache.FlushFile();
	CacheItemInfo.WriteToFile(InfoFileName);
}

bool tContentCacheItem::CanBeRelease()
{
	AUTO_MUTEX lock(hMutex);
	if (RefCount() == 0)
		return true;
	else
		return false;
}

void tContentCacheItem::MarkAsDelete()
{
	AUTO_MUTEX lock(hMutex);
	m_DeleteMark = true;
}

bool tContentCacheItem::CanBeDelete()
{
	AUTO_MUTEX lock(hMutex);
	return m_DeleteMark;
}

void tContentCacheItem::Hit()
{
	AUTO_MUTEX lock(hMutex);
	CacheItemInfo.Usage++;
}

unsigned int tContentCacheItem::GetCachedDataSize()
{
	
	return CacheItemInfo.CacheDataSize + MemoryWriteCache.GetCachedSize();
}

tContentCacheMgr::tContentCacheMgr():
tBaseInterface("tContentCacheMgr"),
CacheSize(DEFAULT_CACHE_SIZE),
MaxCacheSize(0)
{
	hMutex = CreateMutex(NULL, FALSE, NULL);
	memset(CachePath, 0, MAX_PATH);
	SetInterface(eIID_IBaseInterface, (IDataCacheManager*)this);
	SetInterface(eIID_DataCacheManager, (IDataCacheManager*)this);
}

 
tContentCacheMgr::~tContentCacheMgr() 
{
	UnInitCacheMgr();
	CloseHandle(hMutex);
}

void tContentCacheMgr::SetCacheSize(UINT64 Size)
{
	CacheSize = Size;
}

void tContentCacheMgr::SetCachePath(const char * Path)
{
	lstrcpynA(CachePath, Path, MAX_PATH);
}

eResult tContentCacheMgr::InitCacheMgr(UINT64 Size, const char * Path) 
{
	AUTO_MUTEX lock(hMutex);

	if (Size != 0)
		SetCacheSize(Size);
	if (Path != NULL)
		SetCachePath(Path);

	int r = _mkdir(CachePath);
	if (r == -1 && errno != EEXIST)
		return RET_ERROR;

	eResult Res = DetermineMaxSize();
	if (Res != RET_OK)
		return Res;
	CheckCacheSize();
	/**** Disable the maintainCacheSpace ****
	LoadAllCacheItems();
	Res = MaintainCacheSpace();
	UnloadAllCacheItems();
	****************************************/
	m_Timer.Start();
	m_TimerID_AutoSave = m_Timer.SetTimer(300000, this, true, 0);
	m_TimerID_AutoUnload = m_Timer.SetTimer(10000, this, true, 0);
	return Res;
}

eResult tContentCacheMgr::UnInitCacheMgr()
{
	AUTO_MUTEX lock(hMutex);

	/**** Disable the maintainCacheSpace ****
	MaintainCacheSpace();	
	****************************************/
	return UnloadAllCacheItems();
}

IDataCacheItem* tContentCacheMgr::CreateCacheItem(tInt128 ContentID, const char * strWho)
{
	AUTO_MUTEX lock(hMutex);

	// Check the existance of this item.
	tContentCacheItem * pItem = FindCacheItem(ContentID);
	if (pItem != NULL)
	{
		// Give it to user. and add the reference.
		IDataCacheItem * pInterface = NULL;
		eResult Res = pItem->GetInterface(eIID_DataCacheItem, (void**)&pInterface, strWho);
		if (Res == RET_OK)
			return pInterface;
		else
			return NULL;	
	}
	else
	{
		// Try load the Cache Item from disk.
		tContentCacheItem *pItem = LoadCacheItem(ContentID);
		if (pItem != NULL)
		{
			// Give it to user. and add the reference.
			IDataCacheItem * pInterface = NULL;
			eResult Res = pItem->GetInterface(eIID_DataCacheItem, (void**)&pInterface, strWho);
			if (Res == RET_OK)
				return pInterface;
			else
				return NULL;
		}
	}

	// Create new item.
	pItem = new tContentCacheItem();
	char infofile[MAX_PATH];
	char datafile[MAX_PATH];
	GetInfoFileName(ContentID, infofile);
	GetDataFileName(ContentID, datafile);
	pItem->InitCacheItem(ContentID, DEFAULT_VOD_SEGSIZE, infofile, datafile);
	
	// Add them to list.
	CacheItemList.push_back(pItem);

	IDataCacheItem * pInterface = NULL;
	eResult Res = pItem->GetInterface(eIID_DataCacheItem, (void**)&pInterface, strWho);
	if (Res == RET_OK)
		return pInterface;
	else
		return NULL;
}

IDataCacheItem * tContentCacheMgr::GetCacheItem(tInt128 ContentID, const char * strWho)
{
	AUTO_MUTEX lock(hMutex);
	std::deque<tContentCacheItem*>::iterator it;
	for (it = CacheItemList.begin(); it != CacheItemList.end(); it++)
	{
		if ((*it)->CacheItemInfo.ContentID == ContentID)
		{
			// Give it to user. and add the reference.
			IDataCacheItem * pInterface = NULL;
			eResult Res = (*it)->GetInterface(eIID_DataCacheItem, (void**)&pInterface, strWho);
			if (Res == RET_OK)
				return pInterface;
			else
				return NULL;
		}
	}
	
	// Try load the Cache Item from disk.
	tContentCacheItem *pItem = LoadCacheItem(ContentID);
	if (pItem != NULL)
	{
		// Give it to user. and add the reference.
		IDataCacheItem * pInterface = NULL;
		eResult Res = pItem->GetInterface(eIID_DataCacheItem, (void**)&pInterface, strWho);
		if (Res == RET_OK)
			return pInterface;
		else
			return NULL;
	}
	// Can not find the Cache itme.
	return NULL;
}

eResult tContentCacheMgr::GetCachedItemInfo(tCacheItemInfo * pCacheItemInfoList, unsigned int * ListLength)
{
	AUTO_MUTEX lock(hMutex);
	if (pCacheItemInfoList == NULL)
		(*ListLength) = 0;
	// Find disk to get all cache item info.
	std::vector<tCacheItemInfo> ItemList;
	WIN32_FIND_DATAA FindFileData;
	char pattern[MAX_PATH];
	GetFullFileName(INFO_FILE_PATTERN, pattern);
	HANDLE hFindFile = FindFirstFileA(pattern, &FindFileData);
	if (hFindFile == INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		if (error = 2)
			return RET_OK;
		return RET_ERROR;
	}
	do
	{
		if (pCacheItemInfoList == NULL)
		{
			// Only do the count.
			(*ListLength)++;
		}
		else
		{
			char infofile[MAX_PATH];
			char datafile[MAX_PATH];
			GetFullFileName(FindFileData.cFileName, infofile);
			InfoName2DataName(infofile, datafile);
			tContentCacheInfo cci;
			eResult Res = cci.LoadFromFile(infofile, true);	
			if (Res == RET_OK)
			{
				tCacheItemInfo cii;
				cii.m_ContentID			= cci.ContentID;
				cii.m_FinalIndex		= cci.FinalIndex;
				cii.m_FinalSegmentSize	= cci.FinalSegmentSize;
				cii.m_SegmentUnitSize	= cci.SegmentUnitSize;
				cii.m_strInfoFilePath	= cci.m_strCacheInfoFile;
				cii.m_Usage				= cci.Usage;
				ItemList.push_back(cii);
			}
		}
	} while (FindNextFileA(hFindFile, &FindFileData));
	FindClose(hFindFile);
	if (pCacheItemInfoList == NULL)
	{
		// don't do any thing, just return the count.
		return RET_OK;
	}
	// Do the Copy
	assert (*ListLength <= (unsigned int)ItemList.size());
	for (unsigned int i = 0; i < ItemList.size() && i < (*ListLength); i++)
	{
		pCacheItemInfoList[i] = ItemList[i];
	}
	return RET_OK;
}

int tContentCacheMgr::NotifyMessage(int NotifyID, long Data1, long Data2, void * DataPtr, int DataSize)
{
	AUTO_MUTEX lock(hMutex);
	if (Data1 == m_TimerID_AutoSave)
	{
		std::deque<tContentCacheItem*>::iterator it_ci;
		for (it_ci = CacheItemList.begin(); it_ci != CacheItemList.end(); it_ci++)
		{
			(*it_ci)->SaveData();
		}
	}
	else if (Data1 == m_TimerID_AutoUnload)
	{
		std::deque<tContentCacheItem*>::iterator it_ci = CacheItemList.begin();
		while (it_ci != CacheItemList.end())
		{
			tContentCacheItem * pItem = *it_ci;
			if (pItem->CanBeRelease())
			{
				pItem->SaveData();
				if (pItem->CanBeDelete())
				{
					// Remove the related files.
					char infofile[MAX_PATH];
					char datafile[MAX_PATH];
					GetInfoFileName(pItem->CacheItemInfo.ContentID, infofile);
					GetDataFileName(pItem->CacheItemInfo.ContentID, datafile);
					remove(infofile);
					remove(datafile);
				}
				delete pItem;
				it_ci = CacheItemList.erase(it_ci);
			}
			else
				it_ci++;
		}
	}
	return 0;
}

tContentCacheItem * tContentCacheMgr::LoadCacheItem(tInt128 ContentID)
{
	AUTO_MUTEX lock(hMutex);

	// Check the existance of this item.
	tContentCacheItem * pItem = FindCacheItem(ContentID);
	if (pItem != NULL)
	{
		return pItem;
	}
	// Load item from file.
	tContentCacheItem * pNewItem = new tContentCacheItem();
	char infofile[MAX_PATH];
	char datafile[MAX_PATH];
	GetInfoFileName(ContentID, infofile);
	GetDataFileName(ContentID, datafile);
	eResult Res = pNewItem->LoadFromFile(infofile, datafile);
	if (Res != RET_OK)
	{
		delete pNewItem;
		return NULL;
	}
	// put new item to list.
	CacheItemList.push_back(pNewItem);
	return pNewItem;
}

eResult tContentCacheMgr::UnloadCacheItem(tInt128 ContentID)
{
	AUTO_MUTEX lock(hMutex);
	std::deque<tContentCacheItem*>::iterator it;
	for (it = CacheItemList.begin(); it != CacheItemList.end(); it++)
	{
		if ((*it)->CacheItemInfo.ContentID == ContentID)
			break;
	}
	if (it == CacheItemList.end())
		return RET_ERROR;
	tContentCacheItem * pItem = (*it);
	// Remove it from the list.
	CacheItemList.erase(it);
	// Save item to file.
	pItem->SaveToFile();
	// Release item.
	delete pItem;
	return RET_OK;
}

eResult tContentCacheMgr::DetermineMaxSize() 
{
	const UINT64 s1M = 1024*1024;
	const UINT64 s50M = 50 * s1M;
	const UINT64 s200M = 200 * s1M;
	const UINT64 s500M = 500 * s1M;
	const UINT64 s600M = 600 * s1M;
	const UINT64 s1G = 1024 * s1M;
	const UINT64 s2G = 2 * s1G;
	const UINT64 s4G = 4 * s1G;
	const UINT64 s8G = 8 * s1G;
	UINT64 disksize[6] = {s8G, s4G,   s2G,   s1G, s500M, 0};
	UINT64 maxsize[6]  = {s2G, s1G, s600M, s200M,  s50M, 0};
	ULARGE_INTEGER ui;
	BOOL Ret = GetDiskFreeSpaceExA(CachePath, &ui, NULL, NULL);
	if (Ret == FALSE)
	{
		MaxCacheSize = 0;
		DWORD error = GetLastError();
		return RET_ERROR;
	}
	else
	{
		for (int i = 0; i < 6; i++)
		{
			if (ui.QuadPart >= disksize[i])
			{
				MaxCacheSize = maxsize[i];
				break;
			}
		}
	}
	return RET_OK;
}

 
eResult tContentCacheMgr::CheckCacheSize() 
{
	if (CacheSize > MaxCacheSize)
	{
		CacheSize = MaxCacheSize;
	}
	return RET_OK;
}

eResult tContentCacheMgr::LoadAllCacheItems() 
{
	//1. Find all files who is cache info file.
	//2. For each file, parse it and create a Cache item.
	//3. For each cacheitem, check its integration.
	WIN32_FIND_DATAA FindFileData;
	char pattern[MAX_PATH];
	GetFullFileName(INFO_FILE_PATTERN, pattern);
	HANDLE hFindFile = FindFirstFileA(pattern, &FindFileData);
	if (hFindFile == INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		if (error = 2)
			return RET_OK;
		return RET_ERROR;
	}
	do
	{
		char infofile[MAX_PATH];
		char datafile[MAX_PATH];
		GetFullFileName(FindFileData.cFileName, infofile);
		InfoName2DataName(infofile, datafile);
		tContentCacheItem * pLoadItem = new tContentCacheItem();
		eResult Res = pLoadItem->LoadFromFile(infofile, datafile);
		if (Res == RET_OK)
		{
			CacheItemList.push_back(pLoadItem);
		}
		else
		{
			delete pLoadItem;
		}
	} while (FindNextFileA(hFindFile, &FindFileData));
	FindClose(hFindFile);
	return RET_OK;
}


eResult tContentCacheMgr::MaintainCacheSpace()
{
	// Make sure all CacheItems have been saved to disk.
	UnloadAllCacheItems();

	// Delete isolated data files.
	WIN32_FIND_DATAA FindFileData;
	char pattern[MAX_PATH];
	GetFullFileName(DATA_FILE_PATTERN, pattern);
	HANDLE hFindFile = FindFirstFileA(pattern, &FindFileData);
	if (hFindFile != INVALID_HANDLE_VALUE)
	{
		do
		{
			char filepath[MAX_PATH];
			GetFullFileName(FindFileData.cFileName, filepath);
			char infofile[MAX_PATH];
			if (DataName2InfoName(filepath, infofile) == RET_OK)
			{
				// test the exist of info file.
				FILE * pTest = NULL;
				fopen_s(&pTest, infofile, "r");
				if (pTest == NULL)
					remove(filepath);
				else 
					fclose(pTest);
			}
		} while (FindNextFileA(hFindFile, &FindFileData));
		FindClose(hFindFile);		
	}

	//Check the All size.
	LoadAllCacheItems();
	UINT64 TotalCacheSize = 0;
	for(unsigned int i = 0; i < CacheItemList.size(); i++)
	{
		TotalCacheSize += CacheItemList[i]->CacheItemInfo.CacheDataSize;
	}
	if (TotalCacheSize > CacheSize)
	{
		UINT64 RemoveSize = TotalCacheSize - CacheSize;
		UINT64 RemovedSize = 0;
		while (RemovedSize < RemoveSize && !CacheItemList.empty())
		{
			std::deque<tContentCacheItem*>::iterator it_del = CacheItemList.begin();
			std::deque<tContentCacheItem*>::iterator it = CacheItemList.begin();
			int LowestUsage = (*CacheItemList.begin())->CacheItemInfo.Usage;
			for (; it != CacheItemList.end(); it++)
			{
				if (LowestUsage < (*it)->CacheItemInfo.Usage)
				{
					it_del = it;
				}
			}
			unsigned int uTruncatedSize = (*it_del)->TruncateCache((unsigned int)(RemoveSize-RemovedSize));
			RemovedSize += uTruncatedSize;
			tInt128 uContentID = (*it_del)->CacheItemInfo.ContentID;
			UnloadCacheItem(uContentID);
			LoadCacheItem(uContentID);
		}
	}


	return RET_OK;
}

 
eResult tContentCacheMgr::UnloadAllCacheItems()
{
	std::deque<tContentCacheItem*>::iterator it;
	for (it = CacheItemList.begin(); it != CacheItemList.end(); it++)
	{
		tContentCacheItem* pItem = (*it);
		pItem->SaveToFile();
		if (pItem->CanBeDelete())
		{
			// Remove the related files.
			char infofile[MAX_PATH];
			char datafile[MAX_PATH];
			GetInfoFileName(pItem->CacheItemInfo.ContentID, infofile);
			GetDataFileName(pItem->CacheItemInfo.ContentID, datafile);
			remove(infofile);
			remove(datafile);
		}
		delete pItem;
	}
	CacheItemList.clear();
	return RET_OK;
}

eResult tContentCacheMgr::DeleteCacheItem(tInt128 ContentID)
{
	AUTO_MUTEX lock(hMutex);
	// Remove the related cache item.
	tContentCacheItem * pItem = FindCacheItem(ContentID);
	if (pItem == NULL)
	{
		pItem = LoadCacheItem(ContentID);
	}
	if (pItem != NULL && pItem->CanBeRelease())
	{
		eResult Res = UnloadCacheItem(ContentID);	
		// Remove the related files.
		char infofile[MAX_PATH];
		char datafile[MAX_PATH];
		GetInfoFileName(ContentID, infofile);
		GetDataFileName(ContentID, datafile);
		int res = remove(infofile);
		res = remove(datafile);
		if (res != 0)
			return RET_ERROR;
		else
			return RET_OK;
	}
	else if (pItem != NULL)
	{
		pItem->MarkAsDelete();
		return RET_OK;
	}
	return RET_ERROR;
}

unsigned int tContentCacheMgr::TruncateCacheItem(tContentCacheItem * pCacheItem, unsigned int uSize)
{
	// We only need to adjust the DataCacheSize overall, the Cache item itself will truncate the
	// disk cache size to desired one we set here.
	unsigned int uCacheSize = pCacheItem->GetDataSize();
	if (uCacheSize >= uSize)
	{
		return pCacheItem->TruncateCache(uSize);
	}
	return uSize;
}

 
tContentCacheItem * tContentCacheMgr::FindCacheItem(tInt128 ContentID)
{
	std::deque<tContentCacheItem*>::iterator it;
	for (it = CacheItemList.begin(); it != CacheItemList.end(); it++)
	{
		if ((*it)->CacheItemInfo.ContentID == ContentID)
			return (*it);
	}
	return NULL;
}

 
eResult tContentCacheMgr::GetInfoFileName(tInt128 ContentID, char * path) 
{
	char lastchar = CachePath[lstrlenA(CachePath) - 1];
	if (lastchar == '\\')
		sprintf_s(path, MAX_PATH, "%s%s.info", CachePath, ContentID.ToString());
	else
		sprintf_s(path, MAX_PATH, "%s\\%s.info", CachePath, ContentID.ToString());
	return RET_OK;
}

 
eResult tContentCacheMgr::GetDataFileName(tInt128 ContentID, char * path) 
{
	char lastchar = CachePath[lstrlenA(CachePath) - 1];
	if (lastchar == '\\')
		sprintf_s(path, MAX_PATH, "%s%s.data", CachePath, ContentID.ToString());
	else
		sprintf_s(path, MAX_PATH, "%s\\%s.data", CachePath, ContentID.ToString());
	return RET_OK;
}

eResult tContentCacheMgr::DataName2InfoName(char * in, char * out)
{
	unsigned int length = lstrlenA(in);
	if (length == 0)
		return RET_ERROR;
	for (unsigned int e = length - 1; e > 0; e--)
	{
		if (in[e] == '.')
		{
			lstrcpynA(out, in, e+1);
			lstrcatA(out, ".info");
			break;
		}
	}
	return RET_OK;
}

eResult tContentCacheMgr::InfoName2DataName(char * in, char * out)
{
	unsigned int length = lstrlenA(in);
	if (length == 0)
		return RET_ERROR;
	for (unsigned int e = length - 1; e > 0; e--)
	{
		if (in[e] == '.')
		{
			lstrcpynA(out, in, e+1);
			lstrcatA(out, ".data");
			break;
		}
	}
	return RET_OK;
}

void tContentCacheMgr::GetFullFileName(const char * filename, char * fullpath) 
{
	char lastchar = CachePath[lstrlenA(CachePath) - 1];
	if (lastchar == '\\')
		sprintf_s(fullpath, MAX_PATH, "%s%s", CachePath, filename);
	else
		sprintf_s(fullpath, MAX_PATH, "%s\\%s", CachePath, filename);
}

#endif //_linux

//====================================================================
// tLiveSegmentCache
//--------------------------------------------------------------------
tLiveSegmentCache::tLiveSegmentCache():
	MaxQueueLength(100)
{
   
}

tLiveSegmentCache::~tLiveSegmentCache()
{
	// Remove all data queued in the queue.
	std::deque<tCachedSegment>::iterator it;
	for (it = SegmentQueue.begin(); it != SegmentQueue.end(); it++)
	{
		it->DeAlloc();
	}
	SegmentQueue.clear();
}

eResult tLiveSegmentCache::ReadSegment(UINT64 Index, tSegment& Segment)
{
	std::deque<tCachedSegment>::iterator it;
	for (it = SegmentQueue.begin(); it != SegmentQueue.end(); it++)
	{
		if (it->Index == Index)
			break;
	}
	if (it == SegmentQueue.end())
	{
		// Check why can not find segment.
		if (SegmentQueue.size() > 0 && SegmentQueue.back().Index < Index)
		{
			return RET_TOO_FAST;
		}
		else
			return RET_ERROR;
	}
	// Copy data.
	Segment = *it;
	return RET_OK;
}

eResult tLiveSegmentCache::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	if (SegmentQueue.size() == MaxQueueLength)
	{
		// Remove the oldest segment.
		SegmentQueue.front().DeAlloc();
		SegmentQueue.pop_front();
	}
	// push new segment to tail of queue.
	SegmentQueue.push_back(Segment);
	return RET_OK;
}

eResult tLiveSegmentCache::IsSegmentExist(UINT64 Index)
{
	std::deque<tCachedSegment>::iterator it;
	for (it = SegmentQueue.begin(); it != SegmentQueue.end(); it++)
	{
		if (it->Index == Index)
			return RET_OK;
	}
	return RET_ERROR;
}

unsigned int tLiveSegmentCache::GetCachedCount()
{
	return (unsigned int)SegmentQueue.size();
}

//====================================================================

tTVItem::tTVItem():
	tBaseInterface("tTVItem"),
	ContentID(0),
	//RefCount(0),
	//Speed(0),
	sock(0),
	m_uHistorySize(0)
{
	SetInterface(eIID_IBaseInterface, (ITVCacheItem*)this);
	SetInterface(eIID_TVCacheItem, (ITVCacheItem*)this);
	SetInterface(eIID_TVServiceCacheInfo, (ITVServiceCacheInfo*)this);
}


tTVItem::~tTVItem()
{
    std::deque<tCachedSegment>::iterator it;
    for (it = SegmentQueue.begin(); it != SegmentQueue.end(); it++)
    {
        it->DeAlloc();
    }

}

eResult tTVItem::ReadSegment(UINT64 Index, tSegment& Segment)
{
	AUTO_MUTEX_2 lock(ResLock);
	return tLiveSegmentCache::ReadSegment(Index, Segment);
}

eResult tTVItem::WriteSegment(UINT64 Index, const tSegment& Segment)
{
	AUTO_MUTEX_2 lock(ResLock);
	m_uHistorySize += (unsigned int)Segment.Size();
	return tLiveSegmentCache::WriteSegment(Index, Segment);
}

void tTVItem::Reset()
{
    AUTO_MUTEX_2 lock(ResLock);
    //Speed = 0;
    //?RefCount = 0;
    std::deque<tCachedSegment>::iterator it;
    for (it = SegmentQueue.begin(); it != SegmentQueue.end(); it++)
    {
        it->DeAlloc();
    }
    SegmentQueue.clear();
	m_uHistorySize = 0;
}

void tTVItem::GetIndexRange(tIndexRange * pIR)
{
	AUTO_MUTEX_2 lock(ResLock);
    if (SegmentQueue.size() > 0)
	{
	    pIR->InsertPair(SegmentQueue.front().Index, SegmentQueue.back().Index);
	}
    else
        pIR->InsertPair(0, 0);
}

/*
void tTVItem::SetSpeed(int speed_set)
{
	AUTO_MUTEX_2 lock(ResLock);
	Speed = speed_set;
}

void tTVItem::ClearSpeed()
{
	AUTO_MUTEX_2 lock(ResLock);
	Speed = 0;
}
*/

void tTVItem::SetContentID(tInt128 ContentID)
{
    AUTO_MUTEX_2 lock(ResLock);
    this->ContentID = ContentID;
}

tInt128 tTVItem::GetContentID()
{
	AUTO_MUTEX_2 lock(ResLock);
	return ContentID;
}

unsigned int tTVItem::GetSegmentUnitSize()
{
	return 0;
}

eResult tTVItem::GetSegment(UINT64 Index, tSegment & Seg)
{
    return ReadSegment(Index, Seg);
}

eResult tTVItem::SetSegment(UINT64 Index, const tSegment & Seg)
{
	return WriteSegment(Index, Seg);
}

eResult tTVItem::DetectSegment(UINT64 Index)
{
    AUTO_MUTEX_2 lock(ResLock);
	return IsSegmentExist(Index);
}

unsigned int tTVItem::GetHistorySize()
{
	return m_uHistorySize;
}

int tTVItem::GetRange(UINT64 * uHead, UINT64 * uTail)
{
    AUTO_MUTEX_2 lock(ResLock);
	if (SegmentQueue.size() > 0)
	{
		*uHead = SegmentQueue.front().Index;
		*uTail = SegmentQueue.back().Index;
	}
	else
		*uHead = *uTail = 0;
	return 0;
}

tTVCacheManager::tTVCacheManager():
tBaseInterface("tTVCacheManager")
{
	// Set interface table to base interface.
	SetInterface(eIID_IBaseInterface, (ITVCacheManager*)this);
	SetInterface(eIID_TVCacheManager, (ITVCacheManager*)this);
}

tTVCacheManager::~tTVCacheManager()
{
	MAP_TV::iterator it_mapTV = m_mapTVItems.begin();
	for (; it_mapTV != m_mapTVItems.end(); it_mapTV++)
		delete it_mapTV->second;
}

ITVCacheItem * tTVCacheManager::GetTVItem(tInt128 iContentID, const char * strWho)
{
    AUTO_MUTEX_2 lock(ResLock);
	MAP_TV::iterator it_mapTV = m_mapTVItems.find(iContentID);
	if (it_mapTV == m_mapTVItems.end())
		return NULL;
	ITVCacheItem * pTVItem = NULL;
	it_mapTV->second->GetInterface(eIID_TVCacheItem, (void**)&pTVItem, strWho);
	return pTVItem;
}

ITVCacheItem * tTVCacheManager::CreateTVItem(tInt128 iContentID, const char * strWho)
{
    AUTO_MUTEX_2 lock(ResLock);
	ITVCacheItem * pItem = GetTVItem(iContentID, strWho);
	if (pItem == NULL)
	{
		tTVItem * pNewItem = new tTVItem();
		pNewItem->SetContentID(iContentID);
		m_mapTVItems[iContentID] = pNewItem;
		pNewItem->GetInterface(eIID_TVCacheItem, (void**)&pItem, strWho);
	}
	return pItem;
}

eResult tTVCacheManager::RemoveTVItem(tInt128 iContentID, const char * strWho)
{
    AUTO_MUTEX_2 lock(ResLock);
	tTVItem * pItem = (tTVItem*)GetTVItem(iContentID, strWho);
	if (pItem != NULL)
	{
	/*
	// Now, don't remove channel, just clear it.
		m_mapTVItems.erase(iContentID);
		delete pItem;
	*/
		pItem->Reset();
	}

	return RET_OK;
}
